#include "ElementDOM.h"

#include "ATL.h"

#include <algorithm>

using namespace std;

const string ElementDOM::null("");

DebugLevelType ElementDOM::interne( BLANK );

/**
 * Constructeur par défaut, permet également de déterminer tous les
 * champs sauf les attributs de l'ElementDOM.
 *
 * @param bal nom de la balise (tag).
 * @param niv est le niveau dans l'arbre DOM.
 * @param data est la valeur du champ de données.
 * @param numero est le numéro de l'ElementDOm au sein du document lu.
 * @param parent numéro du parent.
 * @param aDonnees \e true si la balise contient des données, false sinon.
 */
ElementDOM::ElementDOM(const string& bal, const unsigned int& niv,
		       const string& data, const unsigned int& numero,
		       const int& parent, const bool& aDonnees)
  : Balise( bal ), Niveau( niv ), Donnees( data ), Numero( numero ),
    Parent( parent ), contientDonnees( aDonnees ), nbrAttributs( 0 )
{}

/**
 * Construteur de copie, effectue un copie profonde.
 */
ElementDOM::ElementDOM(const ElementDOM& dom)
  : Balise( dom.balise() ), Niveau( dom.niveau() ), Donnees( dom.donnees() ),
    Numero( dom.numero() ), Parent( dom.parent() ),
    contientDonnees( dom.aDonnees() ), nbrAttributs( dom.nbrAttributs )
    
{
  Attributs = dom.Attributs;
}

/**
 * Aucune allocation dynamique effectuée dans cette classe, rie à
 * faire ici.
 */
ElementDOM::~ElementDOM()
{}

/**
 * Permet de fixer le nom de la balise
 *
 * @param bal nom de la balise
 */
void ElementDOM::setBalise(const string& bal)
{
  Balise = bal;
}

/**
 * Permet de fixer le niveau de l'ElementDOM
 *
 * @param lvl niveau de l'ElementDOM
 */
void ElementDOM::setNiveau(const unsigned int& lvl)
{
  Niveau = lvl;
}

/**
 * Permet de fixer la valeur du champ de données
 *
 * @param data valeur du champ de données
 */
void ElementDOM::setDonnees(const string& data)
{
  Donnees = data;
}

/**
 * Permet de fixer le numéro
 *
 * @param numero numéro de l'ElementDOM
 */
void ElementDOM::setNumero(const unsigned int& numero)
{
  Numero = numero;
}

/**
 * Permet de fixer le parent
 *
 * @param parent parent de l'ElementDOM
 */
void ElementDOM::setParent(const int& parent)
{
  Parent = parent;
}

/**
 * Permet de changer la valeur du booléen qui gère la présence de
 * données
 *
 * @param aDonnees \e true si oui, \e false dans le cas contraire.
 */
void ElementDOM::setADonnees(const bool& aDonnees)
{
  contientDonnees = aDonnees;
}

/**
 * Permet d'ajouter un attribut ainsi que sa valeur associée à
 * l'ElementDOM.\n
 *
 * Les niveaux de sorties suivant permettent d'afficher les informations :\n
 * FUNC début et fin de méthode\n
 * DEBUG récapitulation de l'élément ajouté
 *
 * @param attrib nom de l'attribut
 * @param val valeur de cet attribut
 */
void ElementDOM::ajoutAttribut(const string& attrib, const string& val)
{
  Debug::empileNiveau( &interne );
  //DebugLevelType etat_precedent( dout.getLevel() );
  //dout.setLevel( interne );
  func() << "ElementDOM::ajoutAttribut -> début" << endreq;

  if ( attrib[0] == ' ' || attrib.empty() || attrib[0] == 0 )
    {
      func() << "ElementDOM::ajoutAttribut -> fin" << endreq;
      Debug::depileNiveau();//dout.setLevel( etat_precedent );
      return;
    }
  else if ( Attributs.find( attrib ) == Attributs.end() )
    {
      debug() << "Ajout attribut " << attrib << ", valeur " << val << endreq;
      nbrAttributs++;
      Attributs[ attrib ] = val;
    }

  func() << "ElementDOM::ajoutAttribut -> fin" << endreq;
  Debug::depileNiveau();//dout.setLevel( etat_precedent );
}

/**
 * Permet de récupérer le nom de la balise
 *
 * @return nom de la balise (tag)
 */
const string& ElementDOM::balise() const
{
  return Balise;
}

/**
 * Permet de récupérer le niveau
 *
 * @return niveau
 */
const unsigned int& ElementDOM::niveau() const
{
  return Niveau;
}

/**
 * Permet de récupérer la valeur du champ de données
 *
 * @return champ de données
 */
const string& ElementDOM::donnees() const
{
  return Donnees;
}

/**
 * Permet de récupérer le numéro
 *
 * @return numero
 */
const unsigned int& ElementDOM::numero() const
{
  return Numero;
}

/**
 * Permet de récupérer le parent
 *
 * @return parent
 */
const int& ElementDOM::parent() const
{
  return Parent;
}

/**
 * Permet de savoir si le champ contien des données ou pas
 *
 * @return \e true si contient des données, \e false sinon
 */
const bool& ElementDOM::aDonnees() const
{
  return contientDonnees;
}

/**
 * Permet de savoir si l'ElementDOM a des attributs
 *
 * @return \e true si oui, \e false sinon
 */
bool ElementDOM::aAttributs() const
{
  return ( Attributs.begin() != Attributs.end() );
}

/**
 * Permet de récupérer le nom de tous les attibuts de l'ElementDOM
 *
 * @return vecteur contenant les noms des attributs
 *
 * @return niveau
 */
vector< string > ElementDOM::attributs() const
{
  // map< string, string >::const_iterator It, Begin( Attributs.begin() ),
  //   End( Attributs.end() );

  vector< string > retour;

  // for (It = Begin; It != End; It++)
  //   retour . push_back( It -> first );

  ExtraireClef< string, string > extraitStr;

  retour.resize( Attributs.size() );

  transform( Attributs.begin(), Attributs.end(), retour.begin(),
	     extraitStr );

  return retour;
}

/**
 * Permet de récupérer la valeur d'un attibut
 *
 * @param attrib nom de l'attribut dont on veut conaître la valeur
 * @return valeur de cet attribut
 */
const string& ElementDOM::attribut(const string& attrib) const
{
  if ( Attributs.find( attrib ) == Attributs.end() )
    return null;
  else
    return Attributs.find( attrib ) -> second;
}


/**
 * Permet de récupérer la valeur d'un attribut
 *
 * @param num numéro de l'attribut désiré
 * @return valeur de cet attribut
 */
const string& ElementDOM::attribut(const unsigned int& num) const
{
  map< string, string >::const_iterator It, Begin( Attributs.begin() ),
    End( Attributs.end() );

  unsigned int i(0);

  for (It = Begin; It != End; It++, i++)
    {
      if ( i == num )
	return It -> second;
    }
  
  return null;
}

/**
 * Opérateur d'affectation, effectue la copie profonde.
 *
 * @param dom ElementDOM à copier
 * @return référence sur l'instance courante, pour pouvoir effecturer
 * A = b = c.
 */
ElementDOM& ElementDOM::operator=(const ElementDOM& dom)
{
  if ( this != &dom )
    {
      Balise          = dom.balise();
      Niveau          = dom.niveau();
      Donnees         = dom.donnees();
      Numero          = dom.numero();
      Attributs       = dom.Attributs;
      Parent          = dom.parent();
      contientDonnees = dom.aDonnees();
      nbrAttributs    = dom.nbrAttributs;
    }
  return *this;
}

/**
 * Permet d'afficher le nom de l'élément, sa valeur ainsi que ses
 * éventuels asstributs.
 *
 * @param os flot de sortie
 * @return référence sur le flot utilisé, pour pouvoir réutiliser le
 * flot sans problème
*/
ostream& ElementDOM::afficheAttributs(ostream& os) const
{
  map< string, string >::const_iterator It, Begin( Attributs.begin() ),
    End( Attributs.end() );

  unsigned int i(0);

  for (It = Begin; It != End; It++, i++)
    {
      os << It -> first << " = " << It -> second;
      if ( i != nbrAttributs - 1 )
	os << ", ";
    }
  return os;
}

/**
 * Permet de régler le niveau de sortie de la classe.
 *
 * @param lvl niveau de sortie
 */
void ElementDOM::setLevel(const DebugLevelType& lvl)
{
  interne = lvl;
}
